<?php
/**
 * class App_Model_Abstract
 *
 * @author kim
 */
class App_Model_Abstract
{
    /**
     * table primary id
     */
    protected $_primary = 'id';

    /**
     * table name
     */
    protected $_name = '';

    /**
     * fetch methods
     */
    protected $_fetchMethods = array(
        'fetchAll',
        'fetchRow',
        'fetchOne',
        'fetchPairs',
        'fetchCol',
    );

    /**
     * adapter of table object
     *
     * Zend_Db_Table
     */
    protected $_table = null;

    /**
     * cache object
     * Zend_Cache_Core
     */
    protected $_cache = null;

    /**
     * get table name
     */
    public function getName()
    {
        return $this->_name;
    }

    /**
     * set table name
     */
    public function setName($name)
    {
        $this->_name = $name;
    }


    /**
     * get table object
     */
    public function getTable($tableName = false)
    {
        if ($tableName) {
            $table = new Zend_Db_Table($tableName);
            $this->setTable($table);
            return $table;
        }
        return $this->_table;
    }

    /**
     * set table object
     */
    public function setTable($table)
    {
        $this->_table = $table;
    }

    /**
     * set cache object
     */
    public function setCache($cache)
    {
        $this->_cache = $cache;
    }

    /**
     * get cache object
     */
    public function getCache()
    {
        return $this->_cache;
    }

    /**
     * get db handler
     *
     * @param string|false $dbName
     * @return App_Model_Abstract
     */
    public function getDb($dbName = false)
    {
        $dbHandler = $this;
        if ($dbName) {
            $bootstrap = Zend_Controller_Front::getInstance()->getParam('bootstrap');
            if ($resource = $bootstrap->getPluginResource('multidb')) {
                if ($db = $resource->getDb($dbName)) {
                    $dbHandler = new self($this->getName());
                    $dbHandler->getTable()->setOptions(array(Zend_Db_Table::ADAPTER => $db));
                }
            }
        }
        return $dbHandler;
    }

    /**
     * construct function
     *
     * 1. set up table object
     * 2. set up cache object
     */
    public function __construct($name = false)
    {
        // set another table name if given
        if (is_string($name)) {
            $this->setName($name);
        }

        if ($this->getName()) {
            $table = new Zend_Db_Table($this->getName());
            if ($this->_primary) {
                $table->setOptions(array(
                    Zend_Db_Table::PRIMARY => $this->_primary
                ));
            }
            $this->setTable($table);
        } else {
            throw new Exception(__CLASS__ . ' : You must assign the table name of current table!');
        }

        if (Zend_Registry::isRegistered('dbcache')) {
            $this->setCache(Zend_Registry::get('dbcache'));
        }
    }

    /**
     * try to fetch data from cache
     *
     * @param string $id the cache id
     * @return false|array
     */
    protected function _fetchCache($id)
    {
        $result = false;
        $cache = $this->getCache();
        if ($cache instanceof Zend_Cache_Core) {
            $result = $cache->load($id);
        }
        return $result;
    }

    /**
     * try to save data into cache
     *
     * @param array $data - the data to be cached
     * @param string $id - the cache id
     * @param int $cacheMins - cache minutes, specificLifetime(in second) = mins * 60
     * @return true
     */
    protected function _saveCache($data, $id, $cacheMins)
    {
        $cache = $this->getCache();
        if ($cache instanceof Zend_Cache_Core) {
            $specificLifetime = $cacheMins * 60;
            $cache->save($data, $id, array(), $specificLifetime);
        }
        return true;
    }

    /**
     * cache for fetch*
     *
     * @param string $method - method name
     * @param array $args - parameters
     * @return array
     */
    public function __call($method, $args)
    {
        if (in_array($method, $this->_fetchMethods) and isset($args[0])) {
            $query = $args[0];
            $bind = isset($args[1]) ? $args[1] : array();
            $cacheMins = isset($args[2]) ? $args[2] : false;

            if ($cacheMins === false) {
                // is no cache
                $db = $this->getTable()->getAdapter();
                $result = $db->$method($query, $bind);
            } else {
                // if having cache minutes
                $cacheMins = intval($cacheMins);
                $cacheId = md5($query);
                $result = $this->_fetchCache($cacheId);
                if (!$result) {
                    $db = $this->getTable()->getAdapter();
                    $result = $db->$method($query, $bind);
                    $this->_saveCache($result, $cacheId, $cacheMins);
                }
            }
            return $result;
        }
    }

    /**
     * quote the sql
     *
     * for example inside your model object :
     * $type = $this->quote($cond['type']);
     * $query .= " AND pt.type_id = $type ";
     *
     * @param string $sql
     * @return string
     */
    public function quote($sql)
    {
        return $this->getTable()->getAdapter()->quote($sql);
    }

    /**
     * Wrap db->query()
     * All bind parameter names must begin with ':'
     *
     * @param string|Zend_Db_Select $sql - The SQL statement with placeholders.
     * @param array $bind - An array of data to bind to the placeholders.
     * @return Zend_Db_Statement_Pdo
     */
    public function query($sql, $bind = array())
    {
        return $this->getTable()->getAdapter()->query($sql, $bind);
    }

    /**
     * get $data[$key]
     * return empty string if not set
     *
     * @param array $data
     * @param string $key
     * @param mixed $else - default return
     * @return mixed
     */
    protected function _g($data, $key, $else = '')
    {
        return (isset($data[$key]) ? $data[$key] : $else);
    }

}
